﻿using System.Collections.Generic;
using System.Xml.Schema;
using XmlStudio.Models.Events;
using XmlStudio.Enums;
using System;
using GuiLabs.Undo;
using System.ComponentModel;
using XmlStudio.DataLayer;
using System.Xml;

namespace XmlStudio.Interfaces {
    /// <summary>
    /// XmlModel interface.
    /// </summary>
    public interface IXmlModel : INotifyPropertyChanged {
        #region Properties
        /// <summary>
        /// Gets the value indicating whether model can undo the last operation.
        /// </summary>
        bool CanUndo { get; }

        /// <summary>
        /// Gets the value indicating whether model can redo the last operation.
        /// </summary>
        bool CanRedo { get; }

        /// <summary>
        /// Gets or sets the value indicating whether the model has been modified thus save is needed before exiting.
        /// </summary>
        bool Modified { get; set; }

        /// <summary>
        ///  Gets the file name of the current document.
        /// </summary>
        string DocumentName { get; }

        /// <summary>
        /// Gets the path to the opened document.
        /// </summary>
        string Path { get; }

        /// <summary>
        /// Gets the number of loaded nodes.
        /// </summary>
        int NodeCount { get; }

        /// <summary>
        /// Gets or sets the application's mode.
        /// </summary>
        ApplicationMode Mode { get; set; }

        /// <summary>
        /// Gets the total node count. For ExpandAll command. This number may have go wrong during document editing. This is intented only for
        /// user warning before expanding everything.
        /// </summary>
        /// <value>The total node count.</value>
        int TotalNodeCount { get; }

        /// <summary>
        /// Gets the document's DOCTYPE.
        /// </summary>
        XmlDocumentType DocumentType { get; }
        #endregion

        #region Methods
        /// <summary>
        /// Opens the document from the specified path.
        /// </summary>
        /// <param name="name">Path to the document.</param>
        void Open(string name);

        /// <summary>
        /// Closes the document.
        /// </summary>
        void Close();

        /// <summary>
        /// Saves the document. If document is newly created, calls SaveAs().
        /// </summary>
        void Save(bool async);

        /// <summary>
        /// Saves the document to the specified path.
        /// </summary>
        /// <param name="path">Path where to save the document.</param>
        /// <param name="async">if set to <c>true</c> [async].</param>
        void Save(string path, bool async);

        /// <summary>
        /// Gets the root element of the document.
        /// </summary>
        /// <returns>Reference to the <see cref="BigXmlNode"/>.</returns>
        BigXmlNode GetRootElement();

        /// <summary>
        /// Sets DOCTYPE.
        /// </summary>
        /// <param name="rootElementName">Name of the root element.</param>
        /// <param name="publicId">Value for PUBLIC attribute (optional).</param>
        /// <param name="systemId">Path to dtd.</param>
        /// <param name="internalSubset">Internal subset (dtd) (optional).</param>
        void SetDocumentType(string rootElementName, string publicId, string systemId, string internalSubset);

        /// <summary>
        /// Removes DOCTYPE.
        /// </summary>
        void RemoveDocumentType();

        /// <summary>
        /// Expands the specified node.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> to expand.</param>
        /// <param name="range">Range to expand.</param>
        /// <param name="wholeSubtree">Indicating whether to expand the whole subtree.</param>
        void ExpandNode(BigXmlNode node, string range = "", bool wholeSubtree = false);

        /// <summary>
        /// Expands all nodes present in the document.
        /// </summary>
        void ExpandAll();

        /// <summary>
        /// Validates DTD asynchronously.
        /// </summary>
        void ValidateDtdAsync();

        /// <summary>
        /// Cancels DTD validation.
        /// </summary>
        void CancelValidateDtd();

        /// <summary>
        /// Validates DTD.
        /// </summary>
        void ValidateDtd();

        /// <summary>
        /// Validates XSD asynchronously.
        /// </summary>
        void ValidateXsdAsync();

        /// <summary>
        /// Cancels XSD validation.
        /// </summary>
        void CancelValidateXsd();

        /// <summary>
        /// Collapses the specified node.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> to collapse.</param>
        void CollapseNode(BigXmlNode node);

        /// <summary>
        /// Gets the number of child elements of node at the specified address.
        /// </summary>
        /// <param name="address">Address of the node.</param>
        /// <returns>Number of child elements.</returns>
        int GetChildCount(int[] address);

        /// <summary>
        /// Counts the number of nodes of the subtree at the specified address.
        /// </summary>
        /// <param name="address">Address of the root of the subtree.</param>
        /// <returns>Number of nodes.</returns>
        int CountNodes(int[] address);

        /// <summary>
        /// Creates new independent <see cref="BigXmlNode" /> which can be inserted inside the document.
        /// </summary>
        /// <param name="prefix">Prefix of the new node.</param>
        /// <param name="name">Name of the new node</param>
        /// <param name="namespaceURI">Namespace uri of the new node.</param>
        /// <returns>Reference to created <see cref="BigXmlNode"/>.</returns>
        BigXmlNode CreateElement(string prefix, string name, string namespaceURI);

        /// <summary>
        /// Creates new independent <see cref="BigXmlNode"/> which can be inserted inside the document.
        /// </summary>
        /// <param name="name">Name of the new node.</param>
        /// <returns>Reference to the created <see cref="BigXmlNode"/>.</returns>
        BigXmlNode CreateElement(string name);

        /// <summary>
        /// Creates text node.
        /// </summary>
        /// <param name="text">Value to set.</param>
        /// <returns>Created text node.</returns>
        BigXmlNode CreateTextNode(string text);

        /// <summary>
        /// Renames the specified element.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> to rename.</param>
        /// <param name="name">New name to set.</param>
        void RenameElement(BigXmlNode node, string name);

        /// <summary>
        /// Renames the specified element.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> to rename.</param>
        /// <param name="localName">New local name.</param>
        /// <param name="namespaceUri">New namespace uri.</param>
        /// <param name="prefix">New prefix.</param>
        void RenameElement(BigXmlNode node, string localName, string namespaceUri, string prefix);

        /// <summary>
        /// Selects nodes according to the specified XPath query.
        /// </summary>
        /// <param name="xpath">XPath query.</param>
        IEnumerable<BigXmlNode> SelectNodes(string xpath);

        /// <summary>
        /// Moves existing subtree to the different position in the xml tree.
        /// </summary>
        /// <param name="subtreeToMove">Root node of the subtree to move.</param>
        /// <param name="newParent">New parent node of the moving subtree.</param>
        /// <param name="insertMode">Insert mode.</param>
        /// <param name="referenceNode">Reference node.</param>
        void MoveSubtree(BigXmlNode subtreeToMove, BigXmlNode newParent, InsertMode insertMode, BigXmlNode referenceNode);

        /// <summary>
        /// Copies existing subtree to the specified destination in the xml tree.
        /// </summary>
        /// <param name="subtreeToCopy">Root of the subtree to copy.</param>
        /// <param name="destination">Parent node where to copy the subtree.</param>
        /// <param name="insertMode">Insert mode.</param>
        /// <param name="referenceNode">Reference node.</param>
        void CopySubtree(BigXmlNode subtreeToCopy, BigXmlNode destination, InsertMode insertMode, BigXmlNode referenceNode);

        /// <summary>
        /// Undoes the last action.
        /// </summary>
        void Undo();

        /// <summary>
        /// Redoes the last undone action.
        /// </summary>
        void Redo();

        /// <summary>
        /// Updates element's attributes to element has only attributes contained in attributes collection.
        /// </summary>
        /// <param name="node">Element where to update attributes.</param>
        /// <param name="attributes">Collection of <see cref="IBigXmlAttribute"/> instances.</param>
        void UpdateAttributes(BigXmlNode node, IDictionary<string, IBigXmlAttribute> attributes);

        /// <summary>
        /// Adds an attribute to the specified node.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> where to add the attribute.</param>
        /// <param name="name">Name of the attribute.</param>
        /// <param name="value">Value of the attribute.</param>
        void AddAttribute(BigXmlNode node, string name, string value);

        /// <summary>
        /// Adds an attribute to the specified node.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> where to add the attribute.</param>
        /// <param name="prefix">Prefix of the attribute.</param>
        /// <param name="localName">Local name of the attribute.</param>
        /// <param name="namespaceUri">Namespace uri of the attribute.</param>
        /// <param name="value">Value of the attrbiute.</param>
        void AddAttribute(BigXmlNode node, string prefix, string localName, string namespaceUri, string value);

        /// <summary>
        /// Removes the specified attribute.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> where to remove the attribute.</param>
        /// <param name="name">Name of the attribute to remove.</param>
        void RemoveAttribute(BigXmlNode node, string name);

        /// <summary>
        /// Removes the specified attribute.
        /// </summary>
        /// <param name="node"><see cref="BigXmlNode"/> where to remove the attribute.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="localName">Local name of the attribute.</param>
        /// <param name="namespaceUri">Namespace uri of the attribute.</param>
        void RemoveAttribute(BigXmlNode node, string prefix, string localName, string namespaceUri);

        /// <summary>
        /// Gets the text representation of the xml document.
        /// </summary>
        /// <returns>String with the raw xml.</returns>
        string GetRawXml();

        /// <summary>
        /// Adds child node to the specified parent node.
        /// </summary>
        /// <param name="parentNode"><see cref="BigXmlNode"/> where to add the child node.</param>
        /// <param name="childNode"><see cref="BigXmlNode"/> to add.</param>
        /// <param name="mode">Insert mode determines where to insert the child node.</param>
        /// <param name="refChild">Reference node for the insert mode.</param>
        void AddChildNode(BigXmlNode parentNode, BigXmlNode childNode, InsertMode mode, BigXmlNode refChild);

        /// <summary>
        /// Creates the new root element.
        /// </summary>
        /// <param name="name">Name of the new root element.</param>
        /// <returns>Reference to the created root element.</returns>
        BigXmlNode CreateRootElement(string name);

        /// <summary>
        /// Creates the new root element.
        /// </summary>
        /// <param name="prefix">Prefix of the root element.</param>
        /// <param name="localName">Local name of the root element.</param>
        /// <param name="namespaceURI">Namespace uri of the element.</param>
        /// <returns>Reference to the created root element.</returns>
        BigXmlNode CreateRootElement(string prefix, string localName, string namespaceURI);

        /// <summary>
        /// Removes the specified node.
        /// </summary>
        /// <param name="nodeToRemove"><see cref="BigXmlNode"/> to remove.</param>
        /// <param name="removeSubtree">Indicates whether to remove the whole subtree with the node or not.</param>
        void RemoveNode(BigXmlNode nodeToRemove, bool removeSubtree);

        /// <summary>
        /// Changes the order of the specified node.
        /// </summary>
        /// <param name="nodoToMove">Reference to the <see cref="BigXmlNode"/> which to move.</param>
        /// <param name="previousSibling">New left sibling of the moved node.</param>
        void ChangeNodeOrder(BigXmlNode nodoToMove, BigXmlNode previousSibling);

        /// <summary>
        /// Modifies the value of text node.
        /// </summary>
        /// <param name="node">Reference to the text node which to modify.</param>
        /// <param name="text">New value to set.</param>
        void ModifyTextNode(BigXmlNode node, string text);

        /// <summary>
        /// Begins transaction of operations.
        /// </summary>
        /// <returns></returns>
        ITransaction BeginTransaction();
        #endregion

        #region Events
        /// <summary>
        /// Occurs when a node was expanded.
        /// </summary>
        event EventHandler<NodeExpandedEventArgs<BigXmlNode>> NodeExpanded;

        /// <summary>
        /// Occurs when a node was collapsed.
        /// </summary>
        event EventHandler<NodeCollapsedEventArgs<BigXmlNode>> NodeCollapsed;

        /// <summary>
        /// Occurs when text representation of the document changes.
        /// </summary>
        event EventHandler<RawXmlChangedEventArgs> RawXmlChanged;

        /// <summary>
        /// Occurs when user wants to save the document but there is no path set.
        /// </summary>
        event EventHandler<DocumentNameRequiredEventArgs> DocumentNameRequired;

        /// <summary>
        /// Occurs when a node was updated.
        /// </summary>
        event EventHandler<NodeUpdatedEventArgs<BigXmlNode>> NodeUpdated;

        /// <summary>
        /// Occurs when a subtree was updated and needs to be refreshed in view.
        /// </summary>
        event EventHandler<SubtreeUpdatedEventArgs<BigXmlNode>> SubtreeUpdated;

        ///// <summary>
        ///// Occurs when a model was modified.
        ///// </summary>
        //event EventHandler<ModelModifiedEventArgs> ModelModified;

        ///// <summary>
        ///// Occurs when document's name changed.
        ///// </summary>
        //event EventHandler<EventArgs> DocumentNameChanged;

        /// <summary>
        /// Occurs when a node was added.
        /// </summary>
        event EventHandler<NodeAddedEventArgs<BigXmlNode>> NodeAdded;

        /// <summary>
        /// Occurs when a node was removed.
        /// </summary>
        event EventHandler<NodeRemovedEventArgs<BigXmlNode>> NodeRemoved;

        /// <summary>
        /// Occurs when a root element was added.
        /// </summary>
        event EventHandler<RootElementAddedEventArgs<BigXmlNode>> RootElementAdded;

        /// <summary>
        /// Occurs when an attribute was added.
        /// </summary>
        event EventHandler<AttributeAddedEventArgs<BigXmlNode, IBigXmlAttribute>> AttributeAdded;

        /// <summary>
        /// Occurs when an attribute was removed.
        /// </summary>
        event EventHandler<AttributeRemovedEventArgs<BigXmlNode, IBigXmlAttribute>> AttributeRemoved;

        /// <summary>
        /// Occurs before document is going close.
        /// </summary>
        event EventHandler<EventArgs> Closing;

        /// <summary>
        /// Occurs when document was closed.
        /// </summary>
        event EventHandler<EventArgs> Closed;

        /// <summary>
        /// Occurs before document is saved.
        /// </summary>
        event EventHandler<EventArgs> Saving;

        /// <summary>
        /// Occurs when document is saved.
        /// </summary>
        event EventHandler<EventArgs> Saved;

        /// <summary>
        /// Occurs when [validating].
        /// </summary>
        event EventHandler<ValidationEventArgs> Validating;

        /// <summary>
        /// Occurs when [status updated].
        /// </summary>
        event EventHandler<StatusUpdatedEventArgs> StatusUpdated;

        /// <summary>
        /// Occurs when [validate XSD completed].
        /// </summary>
        event EventHandler<RunWorkerCompletedEventArgs> ValidateXsdCompleted;

        /// <summary>
        /// Occurs when [validate DTD completed].
        /// </summary>
        event EventHandler<RunWorkerCompletedEventArgs> ValidateDtdCompleted;

        /// <summary>
        /// Occurs when [nodes selected].
        /// </summary>
        event EventHandler<NodesSelectedEventArgs<BigXmlNode>> NodesSelected;

        /// <summary>
        /// Occurs when [root element changed].
        /// </summary>
        event EventHandler<RootElementChangedEventArgs> RootElementChanged;

        /// <summary>
        /// Occurs when [progress changed].
        /// </summary>
        event EventHandler<ProgressChangedEventArgs> ProgressChanged;

        /// <summary>
        /// Occurs when [save completed].
        /// </summary>
        event EventHandler SaveCompleted;
        #endregion
    }
}
